/*================================================================
 *   Copyright (C) 2021 All rights reserved.
 *   
 *   文件名称：config.h
 *   创 建 者：汉森伯逸
 *   创建日期：2021年04月27日
 *   描    述：
 *
 *================================================================*/
#pragma once

#include <iostream>
#include <memory>
#include <functional>
#include <sstream>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>

#include <boost/lexical_cast.hpp>
#include <yaml-cpp/yaml.h>
#include <functional>

#include "log.h"
#include "thread.h"
#include "mutex.h"


namespace hsby{

class ConfigVarBase{
public:
    typedef std::shared_ptr<ConfigVarBase> Ptr;

    ConfigVarBase(const std::string &mName, const std::string &mDescription) : m_name(mName),
                                                                               m_description(mDescription) {
        std::transform(m_name.begin(), m_name.end(), m_name.begin(), ::tolower);
    }
    virtual ~ConfigVarBase(){}

    const std::string &GetName() const {
        return m_name;
    }

    const std::string &GetDescription() const {
        return m_description;
    }

    virtual std::string ToString() = 0;
    virtual bool FromString(const std::string&) = 0;
    virtual std::string GetTypename() const  = 0;
private:
    std::string m_name;
    std::string m_description;
};


/*
 * @brief 配置支持基本类 basic-->basic
 */
template<typename F, typename T>
class LexicalCast{
public:
    T operator()(const F& v){
        return boost::lexical_cast<T>(v);
    }
};

/*
 * @brief 配置支持vector string-->vector
 */
template<typename T>
class LexicalCast<std::string, std::vector<T>>{
public:
    std::vector<T> operator()(std::string str){
        YAML::Node node = YAML::Load(str);
        typename std::vector<T> vec;
        std::stringstream ss;
        for(auto i : node){
            ss.str("");
            ss << i;
            vec.push_back(LexicalCast<std::string, T>()(ss.str()));
        }
        return vec;
    }
};

/*
 * @brief 配置支持vector vector-->string
 */
template<typename F>
class LexicalCast<std::vector<F>, std::string>{
public:
     std::string operator()(std::vector<F> vec){
        YAML::Node node;
        for(auto& it : vec){
            // 把容器中每个F转换成string放回node
            node.push_back(YAML::Node(LexicalCast<F, std::string>()(it)));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

/*
* @brief 配置支持list string-->list
*/
template<typename T>
class LexicalCast<std::string, std::list<T>>{
public:
    std::list<T> operator()(std::string str){
        YAML::Node node = YAML::Load(str);
        typename std::list<T> vec;
        std::stringstream ss;
        for(auto i : node){
            ss.str("");
            ss << i;
            vec.push_back(LexicalCast<std::string, T>()(ss.str()));
        }
        return vec;
    }
};

/*
* @brief 配置支持list list-->string
*/
template<typename F>
class LexicalCast<std::list<F>, std::string>{
public:
    std::string operator()(std::list<F> vec){
        YAML::Node node;
        for(auto& it : vec){
            // 把容器中每个F转换成string放回node
            node.push_back(YAML::Node(LexicalCast<F, std::string>()(it)));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

/*
* @brief 配置支持set string-->set
*/
template<typename T>
class LexicalCast<std::string, std::set<T>>{
public:
    std::set<T> operator()(std::string str){
        YAML::Node node = YAML::Load(str);
        typename std::set<T> st;
        std::stringstream ss;
        for(auto i : node){
            ss.str("");
            ss << i;
            st.insert(LexicalCast<std::string, T>()(ss.str()));
        }
        return st;
    }
};

/*
* @brief 配置支持set set->string
*/
template<typename F>
class LexicalCast<std::set<F>, std::string>{
public:
    std::string operator()(std::set<F> st){
        YAML::Node node;
        for(auto& it : st){
            // 把容器中每个F转换成string放回node
            node.push_back(YAML::Node(LexicalCast<F, std::string>()(it)));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};


/*
* @brief 配置支持unordered_set string-->unordered_set
*/
template<typename T>
class LexicalCast<std::string, std::unordered_set<T>>{
public:
    std::unordered_set<T> operator()(std::string str){
        YAML::Node node = YAML::Load(str);
        typename std::unordered_set<T> st;
        std::stringstream ss;
        for(auto i : node){
            ss.str("");
            ss << i;
            st.insert(LexicalCast<std::string, T>()(ss.str()));
        }
        return st;
    }
};

/*
* @brief 配置支持unordered_set unordered_set->string
*/
template<typename F>
class LexicalCast<std::unordered_set<F>, std::string>{
public:
    std::string operator()(std::unordered_set<F> st){
        YAML::Node node;
        for(auto& it : st){
            // 把容器中每个F转换成string放回node
            node.push_back(YAML::Node(LexicalCast<F, std::string>()(it)));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

/*
* @brief 配置支持map string-->map
*/
template<typename T>
class LexicalCast<std::string, std::map<std::string, T>>{
public:
    std::map<std::string, T> operator()(std::string str){
        YAML::Node node = YAML::Load(str);
        typename std::map<std::string, T> mp;
        std::stringstream ss;
        for(auto i : node){
            ss.str("");
            ss << i.second;
            mp.insert(std::make_pair(i.first.Scalar(), LexicalCast<std::string, T>()(ss.str())));
        }
        return mp;
    }
};

/*
* @brief 配置支持map map->string
*/
template<typename F>
class LexicalCast<std::map<std::string, F>, std::string>{
public:
    std::string operator()(std::map<std::string, F> mp){
        YAML::Node node;
        for(auto& it : mp){
            // 把容器中每个F转换成string放回node
            node[it.first] = YAML::Load(LexicalCast<F, std::string>()(it.second));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};


/*
* @brief 配置支持unordered_map string-->unordered_map
*/
template<typename T>
class LexicalCast<std::string, std::unordered_map<std::string, T>>{
public:
    std::unordered_map<std::string, T> operator()(std::string str){
        YAML::Node node = YAML::Load(str);
        typename std::unordered_map<std::string, T> mp;
        std::stringstream ss;
        for(auto i : node){
            ss.str("");
            ss << i.second;
            mp.insert(std::make_pair(i.first.Scalar(), LexicalCast<std::string, T>()(ss.str())));
        }
        return mp;
    }
};

/*
* @brief 配置支持unordered_map unordered_map->string
*/
template<typename F>
class LexicalCast<std::unordered_map<std::string, F>, std::string>{
public:
    std::string operator()(std::unordered_map<std::string, F> mp){
        YAML::Node node;
        for(auto& it : mp){
            // 把容器中每个F转换成string放回node
            node[it.first] = YAML::Load(LexicalCast<F, std::string>()(it.second));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

/*
 * @brief 配置元素将使用FromStr和ToStr进行转换
 */
template<typename T, class FromStr = LexicalCast<std::string, T>,
                    class ToStr = LexicalCast<T, std::string>>
class ConfigVar : public ConfigVarBase{
public:
    typedef RWMutex RWMutexType;
    typedef std::shared_ptr<ConfigVar> Ptr;
    typedef std::function<void (const T& old_value, const T& new_value)> cb_on_change;
    typedef T ValType;

    ConfigVar(const std::string& name,
              const T& value,
              const std::string& description = "")
              : ConfigVarBase(name, description),
                m_val(value){
    }

    /*
     * @brief 将m_val强制转化为string并返回
     */
    std::string ToString() override {
        try {
            RWMutexType::WriteLockGuardType lock(m_mutex);
            return ToStr()(m_val);
        } catch (std::exception& e){
            HSBY_LOG_SYSTEM_ERROR << "ConfigVar::ToString exception "
                << e.what() << " convert: " << typeid(m_val).name() << " to string";
        }
        return "";
    }

    bool FromString(const std::string& str) override {
        try {
            SetValue(FromStr()(str));
        } catch (std::exception& e){
            HSBY_LOG_SYSTEM_ERROR << "ConfigVar::FromString exception "
                                            << e.what() << " convert: string \"" << str << "\" to " << typeid(m_val).name();
        }
        return false;
    }

    std::string GetTypename() const override { return typeid(T).name();}

    /*
     * @brief 返回m_val
     */
    const T GetValue() {
        RWMutexType::ReadLockGuardType lock(m_mutex);
        return m_val;
    }

    void SetValue(T val)  {
        {
            RWMutexType::ReadLockGuardType lock(m_mutex);
            if (m_val == val) return;

            /*
             * @brief 在这里触发回调
             */
            for (auto &pr : m_cbs) {
                pr.second(m_val, val);
            }
        }
        RWMutexType::WriteLockGuardType lock(m_mutex);
        m_val = val;
    }

    uint64_t AddListener(cb_on_change cb){
        static uint64_t s_fun_id = 0;
        RWMutexType::WriteLockGuardType lock(m_mutex);
        m_cbs[++s_fun_id] = cb;
        return s_fun_id;
    }

    void DelListener(uint64_t key){
        RWMutexType::WriteLockGuardType lock(m_mutex);
        m_cbs.erase(key);
    }

    void ClearListener(){
        RWMutexType::WriteLockGuardType lock(m_mutex);
        m_cbs.clear();
    }

    cb_on_change GetListener(uint64_t key){
        RWMutexType::ReadLockGuardType lock(m_mutex);
        auto & it = m_cbs.find(key);
        return it == m_cbs.end() ? nullptr : it;
    }

private:
    RWMutexType m_mutex;
    T m_val;
    std::map<uint64_t, cb_on_change> m_cbs;
};

class Config{
public:
    typedef RWMutex RWMutexType;
    typedef std::map<std::string, ConfigVarBase::Ptr> ConfigVarMap;

    static ConfigVarBase::Ptr LookupBase(const std::string& name);
    static void LoadFromYaml(const YAML::Node& root);
    static void Visit(std::function<void(ConfigVarBase::Ptr)> cb);

    /*
     * @brief 按照名字查找，如果存在立即返回，不存在则检查名字合法性后创建并返回
     */
    template<typename T>
    static typename ConfigVar<T>::Ptr Lookup(const std::string name,
                                             const T default_value,
                                             std::string description= ""){
        RWMutexType::WriteLockGuardType lock(GetMutex());
        auto it = GetDatas().find(name);
        if (it != GetDatas().end()){
            auto tmp = std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
            // 如果转换失败说明类型不对
            if (tmp){
                HSBY_LOG_SYSTEM_INFO << "Lookup name=" << name << " exists";
                return tmp;
            }else{
                HSBY_LOG_SYSTEM_ERROR << "Lookup name=" << name << "exists but type not" << typeid(T).name()
                                                        << " real typename=" << it->second->GetTypename();
                return nullptr;
            }
        }

        if (name.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._01234567890") != std::string::npos){
            HSBY_LOG_SYSTEM_ERROR << "Lookup name invalid " << name;
            throw std::invalid_argument(name);
        }

        typename ConfigVar<T>::Ptr  v(new ConfigVar<T>(name, default_value, description));
        GetDatas()[name] = v;
        return v;
    }

    template<typename T>
    static typename ConfigVar<T>::Ptr Lookup(const std::string name){
        RWMutexType::ReadLockGuardType lock(GetMutex());
        auto it = GetDatas().find(name);
        if (it == GetDatas().end()){
            return nullptr;
        }
        return std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
    }

private:

    /**
     * @brief Config被别的全局变量初始化函数调用，全局变量初始化没有严格顺序，所以使用这种懒加载的方式定义类静态变量
     */
    static ConfigVarMap& GetDatas(){
        static ConfigVarMap s_datas;
        return s_datas;
    }

    /**
     * @brief Config被别的全局变量初始化函数调用，全局变量初始化没有严格顺序，所以使用这种懒加载的方式定义类静态变量
     */
    static RWMutexType& GetMutex() {
        static RWMutexType s_mutex;
        return s_mutex;
    }
};

} // namespace hsby